# --------------------------------------------------------------------------------------------------------------------
# Project:             Digital Payment System (DPS)
# Website:             http://code.google.com/p/dps-x509/
# Purpose of document: This is the main entry point into the registar web site
# --------------------------------------------------------------------------------------------------------------------
from google.appengine.ext import db, webapp
from google.appengine.ext.webapp import util
from time import strftime

import sys
import string
import urllib

#load our vendor packages
sys.path.insert(0, "simplejson")
sys.path.insert(1, "pyasn1")
sys.path.insert(2, "keyczar")
sys.path.insert(3, "dps")
sys.path.insert(4, "testing")

try:
  import simplejson as json
except ImportError:
  import json
  
import dps.utils
import dps.errors
import dps.x509Certificate
import dps.x509Helper
import dps.x509CertificateParams

import testing.testClient

import model
import certificateChain

# --------------------------------------------------------------------------------------------------------------------
def isCertificateInRegistrar(cert):
  """ returns true if the certificate is found in the registrar
  @param certificate in the form of dps.x509Certificate.X509Certificate 
  @returns db key or None if not found """
  dps.utils.assertion(isinstance(cert, dps.x509Certificate.X509Certificate), \
                      "isCertificateInRegistrar: Incorrect cert type")
  certs = model.Certificate.all()
  base64 = cert.asBase64()
  
  for c in certs:
    if c.base64RawCertificate_ == base64:
      return c
  return None

# --------------------------------------------------------------------------------------------------------------------
def getAllCertificateChains():
  """ find all certificate chains in the database """
  allCerts = model.Certificate.all()
  
  certificateChains = []
  #find top of chain
  for cert in allCerts:
    if cert.isActive_ and cert.isTokenCertificate_: 
      certificateChains.append(certificateChain.CertificateChain(cert))
      
  return certificateChains

# --------------------------------------------------------------------------------------------------------------------
def getAllCertificateChainsContainingCertificate(certBase64):
  certificateChains = getAllCertificateChains()
  
  matches = []
  for chain in certificateChains:
    if chain.isCertificateInChain(certBase64):
      matches.append(chain)
      
  return matches
  
# --------------------------------------------------------------------------------------------------------------------
def getAllCertificateChainsMatchingParams(params):
  certificateChains = getAllCertificateChains()
  
  matches = []
  for chain in certificateChains:
    if chain.doParamsMatchChain(params):
      matches.append(chain)
      
  return matches

# --------------------------------------------------------------------------------------------------------------------
def standardHeader():
  """ at the top of each html page"""
  return "<html><h1>Registrar</h1><hr/>"

# --------------------------------------------------------------------------------------------------------------------
def standardFooter():
  """ at the bottom of each html page"""
  return """<hr/>
            <table>
              <tr>
                <td>&middot</td><td><a href='/registrar/'>Home</a></td>
                <td>&middot</td><td><a href='/registrar/upload'>Upload</a></td>
                <td>&middot</td><td><a href='/registrar/search'>Search</a></td>
				<!--<td><a href='/registrar/clear'>Clear</a></td>-->
                <!--<td>&middot</td><td><a href='/registrar/unitTest'>Unit tests</a></td>-->
                <td>&middot</td><td><a href='http://code.google.com/p/dps-x509/wiki/RegistrarPrototypeDescription'>About</a></td>
                <td>&middot</td><td><a href='https://appengine.google.com/dashboard?app_id=dps-x509'>Admin</a></td><td>&middot</td>
              </tr>
            <table></html>"""

# --------------------------------------------------------------------------------------------------------------------
class DpsHandler(webapp.RequestHandler):
  """ base class for web page handlers """
  
  def get(self):
    self.redirect("/registrar/")
    
  def post(self):
    self.error(404)
    self.response.out.write("No post available for this address")


# --------------------------------------------------------------------------------------------------------------------
class MainHandler(DpsHandler):
  
  def get(self):
    self.redirect("/registrar/")
    
                            
# --------------------------------------------------------------------------------------------------------------------
class RegistrarHandler(DpsHandler):
  """ home page """
  
  def get(self):
    self.response.out.write("""%s<h2>What do you want to do?</h2>
                               <ul>
                                  <li><a href='/registrar/upload'>Upload</a> a certificate</li>
                                  <li><a href='/registrar/search'>Search</a> for a certificate</li>
								  <!--<li><a href='/registrar/clear'>Clear</a> all certificates <b>TEST ONLY FEATURE</b></li>-->
                                  <!--<li>Run the <a href='/registrar/unitTest'>unit tests</a></li>-->
                                  <li>View documentation <a href='http://code.google.com/p/dps-x509/wiki/RegistrarPrototypeDescription'>about</a> this Registrar prototype</li>
                                  <li>Go to the Google App Engine <a href='https://appengine.google.com/dashboard?app_id=dps-x509'>admin</a> console</li>
                               </ul></html>""" % standardHeader())

# --------------------------------------------------------------------------------------------------------------------
class UploadHandler(DpsHandler):
  """ upload certificate to the registrar """

# --------------------------------------------------------------------------------------------------------------------
  def __uploadPage(self, msg=""):
    return """%s<h2>Certificate upload</h2>%s<br/>
                <form method='post' action='/registrar/upload' enctype='multipart/form-data'>
                  <table>
                   <tr><td><input name='certificate' type='file'/></td></tr>
                   <tr><td>or:</td></tr>
                   <tr><td><textarea name='certificateText' cols='80' rows='20'></textarea></td></tr>
                   <tr><td><input type='submit' value='Submit'/></td></tr>
                  </table>
                  <input name='format' type='hidden' value='html'/>
                </form>%s""" % (standardHeader(), msg, standardFooter())

# --------------------------------------------------------------------------------------------------------------------
  def __getPublicKey(self, cert):
    """ find the public key in the store
    @param certificate in the form of dps.x509Certificate.X509Certificate
    @returns db key, public key 
    where public key is in the form of keyczar.keys.RsaPublicKey or None if not found """
    dps.utils.assertion(isinstance(cert, dps.x509Certificate.X509Certificate), \
                        "UploadHandler.__getPublicKey: Incorrect cert type")   
    certs = model.Certificate.all()
    issuerBase64 = cert.getIssuerAsBase64()
    key = None
    pubKey = None
    for c in certs:
      if issuerBase64 == c.subjectBase64_:
        subcert = dps.x509Certificate.X509Certificate.load(c.base64RawCertificate_)
        key = c.key()
        pubKey = subcert.getParams().getPublicKey()
        break
    return key, pubKey
    

# --------------------------------------------------------------------------------------------------------------------
  def get(self):
    self.response.out.write(self.__uploadPage())

# --------------------------------------------------------------------------------------------------------------------
  def post(self):
    """ see http://code.google.com/p/dps-x509/wiki/RegistrarPrototypeDescription for the complete list of return codes """
    result = ""
    try:
      dps.utils.out(self.request.get("certificate"))
      certData = urllib.unquote(self.request.get("certificate")) or urllib.unquote(self.request.get("certificateText"))
      if certData == "":
        dps.utils.out("UploadCertificateHandler.post: No data input")
        raise dps.errors.NoDataSuppliedError()

      tempCertChain = dps.x509Helper.certsToBase64(certData)
      
      # certs are in order from recently allocated to issuer
      certChain = []
      for c in tempCertChain:
        certChain.append(dps.x509Certificate.X509Certificate.load(c))
      
      certChain.reverse() #now we can work from issuer out to the most recently allocated
      for i in range(len(certChain)-1):        
        certChain[i+1].setIssuerCertificate(certChain[i]) 
      baseCert = certChain[0]
      leafCert = certChain[len(certChain)-1]
      
      posInChain = 0
      dbParentKey = None
      isTokenCertificate = False
      for i in range(len(certChain)):
        currentCert = certChain[i]
        
        isInRegKey = isCertificateInRegistrar(currentCert)        
        if isInRegKey:
          if currentCert == leafCert:
            raise dps.errors.CertificateAlreadyInRegistrarError("certificate in chain: %d of %d" % (i, len(certChain)))
          else:
            dbParentKey = isInRegKey.key()
            posInChain = isInRegKey.positionInChain_ + 1
            isTokenCertificate = isInRegKey.isTokenCertificate_
            continue
        
        dbParentCert = None
        publicKey = None
        if currentCert.isSelfSigned():
          publicKey = currentCert.getParams().getPublicKey()
        elif dbParentKey == None:
          dbParentKey, publicKey = self.__getPublicKey(currentCert)
        
        if not dbParentKey == None:
          dbParentCert = db.get(dbParentKey)
          publicKey = dps.x509Certificate.X509Certificate.load(dbParentCert.base64RawCertificate_).getParams().getPublicKey()
        
        if not publicKey:
          publicKey = currentCert.getIssuerPublicKey()
        if not publicKey:
          raise dps.errors.IssuerPublicKeyNotFoundError("certificate in chain: %d of %d" % (i, len(certChain)))
        
        if not currentCert.verifySignature(publicKey):
          raise dps.errors.InvalidSignatureError()
        
        if not dbParentCert == None:
          if dbParentCert.isActive_: #it is no longer active
            dbParentCert.isActive_ = False
            dbParentCert.put()
            posInChain = dbParentCert.positionInChain_ + 1
          elif dbParentCert.isTokenCertificate_: #if it is an issuer we don't care if it is active or not
            raise dps.errors.DoubleSpendError("certificate in chain: %d of %d" % (i, len(certChain)))
        
        params = currentCert.getParams()
        hasTokenData = params.hasCompleteTokenData()
        dps.utils.out("param data: %s is complete: %s" % (str(params), hasTokenData))
        isTokenCertificate = hasTokenData or (dbParentCert and dbParentCert.isTokenCertificate_)
        
        c = model.Certificate()
        
        c.issuerCertificate_    = dbParentKey
        c.base64RawCertificate_ = currentCert.asBase64()
        c.issuer_               = str(params.getIssuer())
        c.subject_              = str(params.getSubject())
        c.subjectBase64_        = currentCert.getSubjectAsBase64()
        c.positionInChain_      = posInChain
        c.isTokenCertificate_   = isTokenCertificate
        c.isActive_             = isTokenCertificate
      
        if hasTokenData:
          c.redeemer_             = params.redeemer_
          c.registrar_            = params.registrar_
          c.exchanger_            = params.exchanger_
          #TODO: throw if registar not = this
          c.tokenType_            = params.tokenType_      
          #python is not supporting float for 2.5 python i have so store cents value of token
          c.tokenQuantity_        = int(100*(params.tokenQuantity_)) 
          
        c.put()
        dbParentKey = c.key()
        
        posInChain += 1
      
      result = "Valid"
      
    except dps.errors.DPSBaseError, e:
      dps.utils.out("Exception in UploadCertificateHandler.post: %s extraInfo: %s" % ((str(sys.exc_info()[0])), e.extraInfo()))
      result = "Rejected:"+e.what()
    
    format = self.request.get("format")
    if format == "html":
      self.response.out.write(self.__uploadPage(result))
    else: #assume json for now
      ret = json.dumps({"returnCode":result})
      dps.utils.out("ret: "+ret)
      self.response.out.write(ret)
 
      
indentPixels = 20
         
# --------------------------------------------------------------------------------------------------------------------
class SearchHandler(DpsHandler):
  
# --------------------------------------------------------------------------------------------------------------------
  def __buildTree(self, dbcerts, currentCert):
    """ build issuer trees """
    tree = {}
    for c in dbcerts:
      sameKey = currentCert == None and c.issuerCertificate_ == None
      if currentCert and c.issuerCertificate_:
        sameKey = currentCert.key() == c.issuerCertificate_.key()
      if sameKey:
        tree[c] = self.__buildTree(dbcerts, c)
    return tree

# --------------------------------------------------------------------------------------------------------------------
  def __getIssuerRows(self, key, children, level):
    row = ""
    if key and not key.isTokenCertificate_:
      subjectStr = key.subject_
      row = """<tr bgcolor='#CBFCBF'><td style='padding-left:%d'>%s</td>
               <td><a href='/registrar/detail?id=%s'>detail</a></td></tr>\n""" % \
          (level * indentPixels, subjectStr, key.key())

    if key:
      level += 1    
    for k in children.keys():
      row += self.__getIssuerRows(k, children[k], level)
    
    return row
    
# --------------------------------------------------------------------------------------------------------------------
  def __getSearchData(self, format="html", params=None):
    """ perform search with params """
    ret = ""
    result = "Rejected:UnknownError"
    chains = []
    try:
      allCerts = model.Certificate.all()
      certs = self.__buildTree(allCerts, None)
      issuerCertStr = self.__getIssuerRows(None, certs, 0)
      
      certificateChains = getAllCertificateChainsMatchingParams(params)
  
      if format == "html":
        tokenCertStr = ""
        for c in certificateChains:
          if c.doParamsMatchChain(params):
            tokenCertStr += c.getTokenHtmlRows(indentPixels)
          
        if not params:
          params = { 
                     "tokenType": "",
                     "tokenQuantity": "",
                     "issuer": "",
                     "subject": "",
                     "redeemer": ""
                   }
    
    
        tokenCerts = self.__buildTree(certs, None)
        ret = """%s<h2>Search Results</h2>
                  <form method='post' action='/registrar/search' enctype='multipart/form-data'>
                  <table>
                    <tr><td>Token Type:</td><td><input name='tokenType' type='text' value='%s'/></td></tr>
                    <tr><td>Quantity:</td><td><input name='tokenQuantity' type='text' value='%s'/></td></tr>
                    <tr><td>Issuer:</td><td><input name='issuer' type='text' value='%s'/><td></tr>
                    <tr><td>Subject:</td><td><input name='subject' type='text' value='%s'/></td></tr>
                    <tr><td>Redeemer:</td><td><input name='redeemer' type='text' value='%s'/></td></tr>
                  </table>
                  <input type='submit' value='Search'/>
                  <input type='hidden' name='format' value='html'/>
                  </form>
                  <h3>Token Certificates</h3>
                  <table border='1'>
                    <tr><th>Token Info</th><th>First Issuer</th><th>Data Added</th><th>Subject</th><th>More Info</th></tr>
                    %s
                  </table>
                  <h3>Issuer Certificates</h3>
                  <table border='1'>
                    <tr><th>Subject</th><th>Detail</th></tr>
                    %s
                  </table>
                  %s""" % \
                  (standardHeader(), params["tokenType"], params["tokenQuantity"], params["issuer"], params["subject"], 
                  params["redeemer"], tokenCertStr, issuerCertStr, standardFooter())
      else: #assume json
        dps.utils.assertion(format == "json", "SearchHandler.__getSearchData: Format must be json or html")
        for c in certificateChains:
          if c.doParamsMatchChain(params):
            chains.append(c.getJsonRepresentation())
        result = "Valid"
    
    except dps.errors.DPSBaseError, e:
      dps.utils.out("Exception in SearchHandler.__getSearchData: " + str(sys.exc_info()[0]))
      result = "Rejected:"+e.what()
    
    except Exception, e:      
      dps.utils.out("Exception in SearchHandler.__getSearchData: " + str(sys.exc_info()[0]) + str(e))
      

    if format == "json":
      ret = json.dumps({"returnCode":result, "certificateChains":chains})
    elif result == "result":
      ret = """%s<h2>Search Results</h2>Database must be reset as items have been unsafely deleted.%s""" % \
                  (standardHeader(), standardFooter())
    return ret
      
  def post(self):
    """ """ 
    
    params = { 
               "tokenType": self.request.get("tokenType"),
               "tokenQuantity": self.request.get("tokenQuantity"),
               "issuer": self.request.get("issuer"),
               "subject": self.request.get("subject"),
               "redeemer": self.request.get("redeemer")
             }
    
    format = self.request.get("format")
    self.response.out.write(self.__getSearchData(format, params))

  def get(self):
    """ """ 
    self.response.out.write(self.__getSearchData("html"))
    
# --------------------------------------------------------------------------------------------------------------------
class DetailHandler(DpsHandler):
  """ Show detail for certificates """
  
  def get(self):
    certEntity = None
    base64Text = formattedBase64Text = prettyPrintText = "Unable to find data"
    jsonRep = {}
    try:
      certId = self.request.get("id")
      certEntity = db.get(certId)
    except:
      pass
    
    matches = []
    if certEntity:
      base64Text = dps.x509Helper.prettyBase64(certEntity.base64RawCertificate_, "<br/>")
      
      #print base64Text
      cert = dps.x509Certificate.X509Certificate.load(certEntity.base64RawCertificate_)
      
      prettyPrintText = "" 
      for line in repr(cert.prettyPrint()).split("\\n"):
        for i in range(0, len(line), 64):
          prettyPrintText += line[i:i+64] + "<br/>"
      prettyPrintText = prettyPrintText.replace("\\n", "<br/>")
      prettyPrintText = prettyPrintText.replace(" ", "&nbsp;")
      
      matchingChains = getAllCertificateChainsContainingCertificate(certEntity.base64RawCertificate_)
      for chain in matchingChains:
        matches.append(chain.getJsonRepresentation())
         
    matchText = []
    i = 1
    for i in range(len(matches)):
      params = matches[i]
      
      tokenDetails = """<tr><th rowspan='5' valign='top'>Certificate Chain #%d</th><th>Token Quantity:</th><td>%s</td></tr>
                        <tr><th>Token Type:</th><td>%s</td></tr>
                        <tr><th>Redeemer:</th><td>%s</td></tr>
                        <tr><th>Exchanger:</th><td>%s</td></tr>
                        <tr><th valign='top'>Certificate Chain:</th><td><tt>%s</tt></td></tr>""" % \
                        (i, params["tokenQuantity"], params["tokenType"], params["redeemer"], params["exchanger"], 
                         params["certificateChain"].replace("\n", "<br/>"))
      matchText.append(tokenDetails)

    if len(matchText) == 0:
      matchText = ["<tr><th>Certificate Chain</th><td colspan='2'><tt>Not in any certificate chains</tt></td></tr>"]
      
    self.response.out.write("""%s<h2>Detail</h2>
                               <table border='1'>
                               <tr><th width='300' valign='top'>ASN.1 representation:</th><td colspan='2'><tt>%s</tt></td></tr>
                               <tr><th valign='top'>Base64 text:</th><td colspan='2'><tt>%s</tt></th></tr>
                               %s
                               </table>%s""" % \
                               (standardHeader(), prettyPrintText, base64Text, "<br/>".join(matchText), standardFooter()))

# --------------------------------------------------------------------------------------------------------------------
class ExactFindHandler(DpsHandler):
  """ find a ceritifcate in the registrar """
  
  def post(self):
    ret = "Rejected:DecodingError"
    try:
      certificate = self.request.get("certificate")
      dps.utils.out("ExactFindHandler.post: %s" % certificate)
      if certificate == "":
        dps.utils.out("ExactFindHandler.post: No data input")
        raise dps.errors.NoDataSuppliedError()
      
      certChain = dps.x509Helper.certsToBase64(certificate)
      if len(certChain) > 1:
        dps.utils.out("ExactFindHandler.post: Too many certificates")
        raise dps.errors.InvalidParametersError()
        
      x509 = dps.x509Certificate.X509Certificate.load(certificate)
      ismatch = isCertificateInRegistrar(x509)
      if ismatch:
        ret = "True"
      else:
        ret = "False"

    except dps.errors.DPSBaseError, e:
      dps.utils.out("Exception in ExactFindHandler.post: " + str(sys.exc_info()[0]))
      ret = "Rejected:"+e.what()
    
    except Exception, e:      
      dps.utils.out("Exception in ExactFindHandler.post: " + str(sys.exc_info()[0]) + str(e))      
    
    #assume json for now...
    result = json.dumps({"returnCode": ret})
    dps.utils.out("ret: "+result)
    self.response.out.write(result) 
    
# --------------------------------------------------------------------------------------------------------------------
class ClearHandler(DpsHandler):
  """ Clear all certificates from the registrar """
  def get(self):
    self.response.out.write("""%s<h2>Clear Registrar</h2>
                               <p>Note this is a <b>TEST ONLY FEATURE</b></p>
                               <form method='post' action='/registrar/clear' enctype='multipart/form-data'>
                                 Are you sure you want to delete all certificates from this registry?<br/>
                                 <input type='submit' value='Clear'/>
                                 <input name='format' type='hidden' value='html'/>
                               </form>%s""" % (standardHeader(), standardFooter()))
  
  def post(self):
    nbefore = model.Certificate.all().count()
    db.delete(model.Certificate.all())
    nafter = model.Certificate.all().count()
    
    isCleared = "True"
    if nafter > 0:
      isCleared = "False"
    dps.utils.out("isCleared: %s before: %d after: %d" % (isCleared, nbefore, nafter))

    format = self.request.get('format')
    if format == "html":
      self.response.out.write("""%s<h2>Registrar Cleared</h2>
                                 <table>
                                 <tr><td>Registry cleared?</td><td>%s</td></tr>
                                 <tr><td># certificates before:</td><td>%d</td></tr>
                                 <tr><td># certificates after:</td><td>%d</td></tr>
                                 </table>%s""" % (standardHeader(), isCleared, nbefore, nafter, standardFooter()))
    else: #assume json for now
      result = json.dumps({"returnCode": isCleared})
      dps.utils.out("ret: "+result)
      self.response.out.write(result)    
    
# --------------------------------------------------------------------------------------------------------------------
class RemoveHandler(DpsHandler):
  """ remove a certificate or certificate chain from the registrar """  
  
  def post(self):
    ret = "Rejected:DecodingError"
    try:  
      format = self.request.get('format')
      dps.utils.assertion(format == "json", "Only json supported at the moment")
      dps.utils.out(self.request.get("certificate"))
      certData = urllib.unquote(self.request.get("certificate"))
      if certData == "":
        dps.utils.out("ExactFindHandler.post: No data input")
        raise dps.errors.NoDataSuppliedError()
        
      certChain = dps.x509Helper.certsToBase64(certData)
          
      certificates = model.Certificate.all()
      nbefore = certificates.count()
      matches = 0
      ncerts = len(certChain)
      
      for c in certificates:
        if c.base64RawCertificate_ in certChain:
          matches += 1 
          c.delete()
      
      nafter = model.Certificate.all().count()
      ret = "NotAllRemovedFromRegistrar"
      allRemoved = "False"
      if nafter == nbefore - matches:
        ret = "AllRemovedFromRegistrar"
        allRemoved = "True"
      dps.utils.out("isRemoved: %s before: %d matches: %d after: %d" % (allRemoved, nbefore, matches, nafter))

    except dps.errors.DPSBaseError, e:
      dps.utils.out("Exception in RemoveHandler.post: " + str(sys.exc_info()[0]))
      ret = "Rejected:"+e.what()
    
    except Exception, e:      
      dps.utils.out("Exception in RemoveHandler.post: " + str(sys.exc_info()[0]) + str(e))      
    
    result = json.dumps({"returnCode": ret})
    dps.utils.out("ret: "+result)
    self.response.out.write(result)
      
# --------------------------------------------------------------------------------------------------------------------
class UnitTestHandler(DpsHandler):
  """ Run unit tests """
  
  def get(self):
    results = testing.testClient.runTests()
    o = "<table>"
    for r in results:
      o += "<tr><td>%s</td></tr>" % r
    o += "</table>"
    self.response.out.write("%s<h2>Unit Tests</h2>%s" % (standardHeader(), o, standardFooter())) 
    
# --------------------------------------------------------------------------------------------------------------------
class GetCertificateChainHandler(DpsHandler):
  """ Retrieve a certificate chain from the registrar """
  
  def post(self):
    returnCode = "Rejected:DecodingError"
    matches = []
    try:
      certificate = self.request.get("certificate")
      if certData == "":
        dps.utils.out("ExactFindHandler.post: No data input")
        raise dps.errors.NoDataSuppliedError()

      x509 = dps.x509Certificate.X509Certificate.load(certificate)
      ismatch = isCertificateInRegistrar(x509)
      if not ismatch:
        returnCode = "CertificateNotInRegistrar"
      else:  
        matchingChains = getAllCertificateChainsContainingCertificate(certificate)
        for chain in matchingChains:
          matches.append(chain.getJsonRepresentation())
            
        returnCode = "Success"

    except dps.errors.DPSBaseError, e:
      dps.utils.out("Exception in UploadCertificateHandler.post: " + str(sys.exc_info()[0]))
      returnCode = "Rejected:"+e.what()
    
    except Exception, e:      
      dps.utils.out("Exception in ExactFindHandler.post: " + str(sys.exc_info()[0]) + str(e))      
    
    #assume json for now...
    result = json.dumps({"returnCode": returnCode, "matchingChains": matches})
    dps.utils.out("ret: "+result)
    self.response.out.write(result)   
    
# --------------------------------------------------------------------------------------------------------------------
def main():
  dps.utils.setLogMode(dps.utils.LogOutputModes.LOG_ONLY)

  application = webapp.WSGIApplication([("/registrar/upload", UploadHandler),
                                       ("/registrar/search", SearchHandler),
                                       ("/registrar/detail", DetailHandler),
                                       ("/registrar/clear", ClearHandler),
                                       ("/registrar/remove", RemoveHandler),
                                       ("/registrar/getCertificateChain", GetCertificateChainHandler),
                                       ("/registrar/exactFind", ExactFindHandler),
                                       ("/registrar/.*",  RegistrarHandler),
                                       ("/.*",  MainHandler)],
                                       debug=True)
  util.run_wsgi_app(application)

if __name__ == "__main__":
  main()
